﻿using System;
using System.Collections;
using System.Text;
using Antlr.Runtime.Tree;
using Antlr.Runtime;
using System.Collections.Generic;

namespace Common.ExpressionParsing
{
    public class Expression
    {
        #region Properties

        // It is needed for tranfer parameters beetwen functions
        public Hashtable Parameters { get; set; }

        // It is needed for inserting external parameters
        public Dictionary<String, String> ExternalParams { get; set; }

        // It is an expression string represintation
        protected string ExpressionString { get; set; }

        // It is for extensions
        public IExtensionManager ExtensionManager { get; set; }

        #endregion Properties


        #region Constructors

        public Expression(string expression, IExtensionManager extensionManager)
            : this(expression)
        {
            ExtensionManager = extensionManager;
        }

        public Expression(string expression)
        {
            if (expression == null || expression == String.Empty)
                throw new
                    ArgumentException("Expression can't be empty", "expression");

            this.ExpressionString = expression;

            ExtensionManager = new DefaultExtensionManager();
        }

        #endregion Constructors


        #region Methods

        protected CommonTree Parse(string expression)
        {
            ECalcLexer lexer = new ECalcLexer(new ANTLRStringStream(expression));
            ECalcParser parser = new ECalcParser(new CommonTokenStream(lexer));

            try
            {
                RuleReturnScope rule = parser.expression();
                if (parser.HasError)
                {
                    throw new EvaluationException(parser.ErrorMessage + " " + parser.ErrorPosition);
                }

                return rule.Tree as CommonTree;
            }
            catch (EvaluationException)
            {
                throw;
            }
            catch (Exception e)
            {
                throw new EvaluationException(e.Message, e);
            }
        }

        public object Evaluate()
        {
            EvaluationVisitor visitor = new EvaluationVisitor();
            visitor.EvaluateFunction += EvaluateFunction;
            visitor.EvaluateParameter += EvaluateParameter;
            visitor.Parameters = Parameters;
            visitor.ExternalParams = ExternalParams;
            visitor.ExtensionManager = ExtensionManager;

            LogicalExpression.Create(Parse(ExpressionString)).Accept(visitor);
            return visitor.Result;
        }

        #endregion Methods


        #region Events

        public event EvaluateFunctionHandler EvaluateFunction;
        public event EvaluateParameterHandler EvaluateParameter;

        #endregion Events
    }
}
